/*
 * Copyright (c) 2023 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "i_blockio.h"
#include <algorithm>
#include <cassert>
#include <cstring>
#include <limits>
#include <mc/log.h>
#include <stdexcept>

namespace dev {

// 序列化类型定义 - 与skynet完全兼容
#define TYPE_NIL          0
#define TYPE_BOOLEAN      1
#define TYPE_NUMBER       2
#define TYPE_NUMBER_ZERO  0
#define TYPE_NUMBER_BYTE  1
#define TYPE_NUMBER_WORD  2
#define TYPE_NUMBER_DWORD 4
#define TYPE_NUMBER_QWORD 6
#define TYPE_NUMBER_REAL  8
#define TYPE_USERDATA     3
#define TYPE_SHORT_STRING 4
#define TYPE_LONG_STRING  5
#define TYPE_TABLE        6
// 注意：移除了TYPE_BINARY，因为原版skynet不支持

#define MAX_COOKIE         32
#define COMBINE_TYPE(t, v) ((t) | (v) << 3)

// 写入缓冲区类
class WriteBuffer {
private:
    std::vector<uint8_t> m_buffer;

public:
    WriteBuffer() = default;

    void push(const void* buf, size_t sz) {
        size_t old_size = m_buffer.size();
        m_buffer.resize(old_size + sz);
        std::memcpy(m_buffer.data() + old_size, buf, sz);
    }

    void push_byte(uint8_t byte) {
        m_buffer.push_back(byte);
    }

    const std::vector<uint8_t>& get_data() const {
        return m_buffer;
    }

    void clear() {
        m_buffer.clear();
    }
};

// 读取缓冲区类
class ReadBuffer {
private:
    const std::vector<uint8_t>& m_data;
    size_t                      m_ptr;

public:
    ReadBuffer(const std::vector<uint8_t>& data) : m_data(data), m_ptr(0) {
    }

    const void* read(size_t sz) {
        if (m_ptr + sz > m_data.size()) {
            return nullptr;
        }
        const void* result = m_data.data() + m_ptr;
        m_ptr += sz;
        return result;
    }

    bool eof() const {
        return m_ptr >= m_data.size();
    }

    size_t remaining() const {
        return m_data.size() - m_ptr;
    }
};

/**
 * 插件请求方法
 *
 * @param context 上下文映射
 * @param plugin_name 插件名称
 * @param plugin_cmd 插件命令
 * @param params 序列化的输入参数
 * @return 序列化的结果数据
 *
 * 返回格式说明：
 * 返回的是序列化后的二进制数据，反序列化后包含两个元素：
 * 1. 第一个元素：bool类型，固定为true，表示操作成功
 * 2. 第二个元素：string类型，包含实际的二进制响应数据（二进制数据序列化为字符串以保持skynet兼容性）
 *
 * 示例反序列化结果：
 * std::vector<seri_value> result = seri_unpack(returned_data);
 * // result[0] 是 bool 类型，值为 true
 * // result[1] 是 std::string 类型，包含129字节的响应数据
 *
 * 注意：二进制数据在序列化时会被转换为字符串格式，使用时需要进行类型转换
 */
std::vector<uint8_t> Interface_BlockIO::PluginRequest(std::map<std::string, std::string> context,
                                                      const std::string&          plugin_name,
                                                      const std::string&          plugin_cmd,
                                                      const std::vector<uint8_t>& params) {
    std::vector<uint8_t> out_buffer;
    // 反序列化输入参数 - 这就是要写入的数据流
    std::vector<seri_value> params_unpack = seri_unpack(params);

    // 将反序列化后的数据转换为二进制格式用于底层调用
    // 这里我们需要从 seri_value 中提取实际的二进制数据
    std::vector<uint8_t> params_binary;
    for (const auto& value : params_unpack) {
        std::visit(
            [&](const auto& v) {
                using T = std::decay_t<decltype(v)>;
                if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
                    // 如果是二进制数据，直接追加
                    params_binary.insert(params_binary.end(), v.begin(), v.end());
                } else if constexpr (std::is_same_v<T, std::string>) {
                    // 如果是字符串，转换为字节
                    params_binary.insert(params_binary.end(), v.begin(), v.end());
                } else if constexpr (std::is_same_v<T, int8_t> || std::is_same_v<T, uint8_t>) {
                    // 如果是字节类型，直接添加
                    params_binary.push_back(static_cast<uint8_t>(v));
                } else {
                    // 其他类型暂时跳过或转换为字节
                    wlog("unsupported type in params_unpack: ${type}", ("type", typeid(v).name()));
                }
            },
            value);
    }

    BusLock lock(*m_chip->get_host_bus());
    out_buffer.clear();
    std::string error;
    auto result = m_chip->plugin_request(plugin_name, plugin_cmd, params_binary, out_buffer, error);
    // 去除前导0
    auto it = out_buffer.begin();
    while (it != out_buffer.end() && *it == 0) {
        ++it;
    }
    std::vector<uint8_t> trimmed_result(it, out_buffer.end());

    if (result != HAS_OK) {
        MC_THROW(mc::method_call_exception, "PluginRequest failed, result: ${result}, error: ${error}",
                 ("result", result)("error", error));
    }

    // 确保out_buffer至少有129字节，不足的补00
    if (trimmed_result.size() < 129) {
        trimmed_result.resize(129, 0x00);
    }
    // 将 out_buffer 包装成 seri_value 进行序列化
    std::vector<seri_value> out_buffer_data = {true, trimmed_result};
    std::vector<uint8_t>    params_pack     = seri_pack(out_buffer_data);

    return params_pack;
}

// 序列化辅助函数
static void wb_nil(WriteBuffer& wb) {
    wb.push_byte(TYPE_NIL);
}

static void wb_boolean(WriteBuffer& wb, bool value) {
    uint8_t n = COMBINE_TYPE(TYPE_BOOLEAN, value ? 1 : 0);
    wb.push_byte(n);
}

static void wb_integer(WriteBuffer& wb, int64_t v) {
    int type = TYPE_NUMBER;
    if (v == 0) {
        uint8_t n = COMBINE_TYPE(type, TYPE_NUMBER_ZERO);
        wb.push_byte(n);
    } else if (v != (int32_t)v) {
        uint8_t n = COMBINE_TYPE(type, TYPE_NUMBER_QWORD);
        wb.push_byte(n);
        wb.push(&v, sizeof(v));
    } else if (v < 0) {
        int32_t v32 = (int32_t)v;
        uint8_t n   = COMBINE_TYPE(type, TYPE_NUMBER_DWORD);
        wb.push_byte(n);
        wb.push(&v32, sizeof(v32));
    } else if (v < 0x100) {
        uint8_t n = COMBINE_TYPE(type, TYPE_NUMBER_BYTE);
        wb.push_byte(n);
        uint8_t byte = (uint8_t)v;
        wb.push(&byte, sizeof(byte));
    } else if (v < 0x10000) {
        uint8_t n = COMBINE_TYPE(type, TYPE_NUMBER_WORD);
        wb.push_byte(n);
        uint16_t word = (uint16_t)v;
        wb.push(&word, sizeof(word));
    } else {
        uint8_t n = COMBINE_TYPE(type, TYPE_NUMBER_DWORD);
        wb.push_byte(n);
        uint32_t v32 = (uint32_t)v;
        wb.push(&v32, sizeof(v32));
    }
}

static void wb_real(WriteBuffer& wb, double v) {
    uint8_t n = COMBINE_TYPE(TYPE_NUMBER, TYPE_NUMBER_REAL);
    wb.push_byte(n);
    wb.push(&v, sizeof(v));
}

static void wb_string(WriteBuffer& wb, const std::string& str) {
    int len = str.length();
    if (len < MAX_COOKIE) {
        uint8_t n = COMBINE_TYPE(TYPE_SHORT_STRING, len);
        wb.push_byte(n);
        if (len > 0) {
            wb.push(str.data(), len);
        }
    } else {
        uint8_t n;
        if (len < 0x10000) {
            n = COMBINE_TYPE(TYPE_LONG_STRING, 2);
            wb.push_byte(n);
            uint16_t x = (uint16_t)len;
            wb.push(&x, 2);
        } else {
            n = COMBINE_TYPE(TYPE_LONG_STRING, 4);
            wb.push_byte(n);
            uint32_t x = (uint32_t)len;
            wb.push(&x, 4);
        }
        wb.push(str.data(), len);
    }
}

// 序列化单个值
static void pack_one(WriteBuffer& wb, const seri_value& value) {
    std::visit(
        [&wb](const auto& v) {
            using T = std::decay_t<decltype(v)>;
            if constexpr (std::is_same_v<T, std::nullptr_t>) {
                wb_nil(wb);
            } else if constexpr (std::is_same_v<T, bool>) {
                wb_boolean(wb, v);
            } else if constexpr (std::is_same_v<T, int8_t>) {
                wb_integer(wb, v);
            } else if constexpr (std::is_same_v<T, uint8_t>) {
                wb_integer(wb, v);
            } else if constexpr (std::is_same_v<T, int16_t>) {
                wb_integer(wb, v);
            } else if constexpr (std::is_same_v<T, uint16_t>) {
                wb_integer(wb, v);
            } else if constexpr (std::is_same_v<T, int32_t>) {
                wb_integer(wb, v);
            } else if constexpr (std::is_same_v<T, uint32_t>) {
                wb_integer(wb, v);
            } else if constexpr (std::is_same_v<T, int64_t>) {
                wb_integer(wb, v);
            } else if constexpr (std::is_same_v<T, uint64_t>) {
                wb_integer(wb, static_cast<int64_t>(v));
            } else if constexpr (std::is_same_v<T, float>) {
                wb_real(wb, v);
            } else if constexpr (std::is_same_v<T, double>) {
                wb_real(wb, v);
            } else if constexpr (std::is_same_v<T, std::string>) {
                wb_string(wb, v);
            } else if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
                // 二进制数据作为字符串处理（skynet兼容）
                std::string str(v.begin(), v.end());
                wb_string(wb, str);
            }
        },
        value);
}

// 反序列化辅助函数
static int64_t get_integer(ReadBuffer& rb, int cookie) {
    switch (cookie) {
    case TYPE_NUMBER_ZERO:
        return 0;
    case TYPE_NUMBER_BYTE: {
        const uint8_t* pn = static_cast<const uint8_t*>(rb.read(sizeof(uint8_t)));
        if (pn == nullptr) {
            throw std::runtime_error("Invalid stream: cannot read byte");
        }
        return *pn;
    }
    case TYPE_NUMBER_WORD: {
        const void* pn = rb.read(sizeof(uint16_t));
        if (pn == nullptr) {
            throw std::runtime_error("Invalid stream: cannot read word");
        }
        uint16_t n;
        std::memcpy(&n, pn, sizeof(n));
        return n;
    }
    case TYPE_NUMBER_DWORD: {
        const void* pn = rb.read(sizeof(int32_t));
        if (pn == nullptr) {
            throw std::runtime_error("Invalid stream: cannot read dword");
        }
        int32_t n;
        std::memcpy(&n, pn, sizeof(n));
        return n;
    }
    case TYPE_NUMBER_QWORD: {
        const void* pn = rb.read(sizeof(int64_t));
        if (pn == nullptr) {
            throw std::runtime_error("Invalid stream: cannot read qword");
        }
        int64_t n;
        std::memcpy(&n, pn, sizeof(n));
        return n;
    }
    default:
        throw std::runtime_error("Invalid stream: unknown number type");
    }
}

static double get_real(ReadBuffer& rb) {
    const void* pn = rb.read(sizeof(double));
    if (pn == nullptr) {
        throw std::runtime_error("Invalid stream: cannot read real");
    }
    double n;
    std::memcpy(&n, pn, sizeof(n));
    return n;
}

static std::string get_string(ReadBuffer& rb, int cookie) {
    if (cookie < MAX_COOKIE) {
        const char* p = static_cast<const char*>(rb.read(cookie));
        if (p == nullptr) {
            throw std::runtime_error("Invalid stream: cannot read short string");
        }
        return std::string(p, cookie);
    } else {
        throw std::runtime_error("Invalid stream: short string too long");
    }
}

static std::string get_long_string(ReadBuffer& rb, int cookie) {
    if (cookie == 2) {
        const void* plen = rb.read(2);
        if (plen == nullptr) {
            throw std::runtime_error("Invalid stream: cannot read string length");
        }
        uint16_t n;
        std::memcpy(&n, plen, sizeof(n));
        const char* p = static_cast<const char*>(rb.read(n));
        if (p == nullptr) {
            throw std::runtime_error("Invalid stream: cannot read long string");
        }
        return std::string(p, n);
    } else if (cookie == 4) {
        const void* plen = rb.read(4);
        if (plen == nullptr) {
            throw std::runtime_error("Invalid stream: cannot read string length");
        }
        uint32_t n;
        std::memcpy(&n, plen, sizeof(n));
        const char* p = static_cast<const char*>(rb.read(n));
        if (p == nullptr) {
            throw std::runtime_error("Invalid stream: cannot read long string");
        }
        return std::string(p, n);
    } else {
        throw std::runtime_error("Invalid stream: invalid long string format");
    }
}

// 反序列化单个值
static seri_value unpack_one(ReadBuffer& rb) {
    uint8_t        type;
    const uint8_t* t = static_cast<const uint8_t*>(rb.read(sizeof(type)));
    if (t == nullptr) {
        throw std::runtime_error("Invalid stream: cannot read type");
    }
    type          = *t;
    int cookie    = type >> 3;
    int base_type = type & 0x7;

    switch (base_type) {
    case TYPE_NIL:
        return std::nullptr_t{};
    case TYPE_BOOLEAN:
        return cookie != 0;
    case TYPE_NUMBER:
        if (cookie == TYPE_NUMBER_REAL) {
            return get_real(rb);
        } else {
            int64_t val = get_integer(rb, cookie);
            // 根据值的大小返回合适的类型
            if (val >= std::numeric_limits<int8_t>::min() &&
                val <= std::numeric_limits<int8_t>::max()) {
                return static_cast<int8_t>(val);
            } else if (val >= 0 && val <= std::numeric_limits<uint8_t>::max()) {
                return static_cast<uint8_t>(val);
            } else if (val >= std::numeric_limits<int16_t>::min() &&
                       val <= std::numeric_limits<int16_t>::max()) {
                return static_cast<int16_t>(val);
            } else if (val >= 0 && val <= std::numeric_limits<uint16_t>::max()) {
                return static_cast<uint16_t>(val);
            } else if (val >= std::numeric_limits<int32_t>::min() &&
                       val <= std::numeric_limits<int32_t>::max()) {
                return static_cast<int32_t>(val);
            } else if (val >= 0 && val <= std::numeric_limits<uint32_t>::max()) {
                return static_cast<uint32_t>(val);
            } else {
                return val;
            }
        }
    case TYPE_USERDATA:
        return std::string("userdata");
    case TYPE_SHORT_STRING:
        return get_string(rb, cookie);
    case TYPE_LONG_STRING:
        return get_long_string(rb, cookie);
    case TYPE_TABLE:
        // 暂时不支持table类型，返回空字符串
        throw std::runtime_error("Table type not supported in this context");
    default:
        throw std::runtime_error("Invalid stream: unsupported type");
    }
}

std::vector<uint8_t> Interface_BlockIO::seri_pack(const std::vector<seri_value>& data) {
    WriteBuffer wb;

    for (const auto& value : data) {
        pack_one(wb, value);
    }

    return wb.get_data();
}

std::vector<seri_value> Interface_BlockIO::seri_unpack(const std::vector<uint8_t>& packed_data) {
    if (packed_data.empty()) {
        return {};
    }

    ReadBuffer              rb(packed_data);
    std::vector<seri_value> result;

    try {
        while (!rb.eof()) {
            result.push_back(unpack_one(rb));
        }
    } catch (const std::exception& e) {
        MC_THROW(mc::method_call_exception, "Failed to unpack data: ${error}", ("error", e.what()));
    }

    return result;
}

} // namespace dev

MC_REFLECT(dev::Interface_BlockIO)
